#include <windows.h>

//#include "mod_man.h"

#include <malloc.h>


extern
void* getMem_C(int size);

extern
void freeMem_C(void* ptr);

extern
void Print(char* s){}

extern
void Error(char* s){}


 
extern int __stdcall init_module(void *pBinCode);
extern void __stdcall set_module_src(void *pBinCode, char *szSrc);
extern int __stdcall fasmCompile( char *szModuleName, 
				char *szFasmFuncToCompile,
				void **ppBinCode,
				int *codeSize
				);



__declspec(dllexport)
void* compile_and_load(char* szSrcFileName, char *szFasmFoo){
	int codeSize = 0;
	void *pBinCode = 0;

	fasmCompile(
		szSrcFileName,
		szFasmFoo,
		&pBinCode,
		&codeSize
		);

	if(pBinCode && codeSize){
		int z;

		VirtualProtect(pBinCode,
				codeSize,
				PAGE_EXECUTE_READWRITE,
				&z
				);
		if(!init_module(pBinCode)){
			freeMem_C(pBinCode);
			return NULL;
		}
		set_module_src(pBinCode, szFasmFoo);
	}else{
		if(pBinCode){
			freeMem_C(pBinCode);
		}
		return NULL;
	}
	return pBinCode;
}




//---------------------------------------------------------------------------
struct Var{
	struct Var *next;

	//---
	char* name;
	void* ptr;
	char* type;
};

struct Var* var_registry = 0;
struct Var* foo_registry = 0;


__declspec(dllexport)
void *register_var(char* szVarName, void* pVar, int varType){
	struct Var* p;
	char* s;

	if(
		szVarName == 0 ||
		pVar == 0
	){
		return 0;
	}
	
	for(p = var_registry; p != 0; p = p->next){
		if(!strcmp(p->name, szVarName))
			return 0;
	}

	p = (struct Var*)getMem_C(sizeof(struct Var));
	p->next = var_registry;
	var_registry = p;

	p->type = (char*)varType;
	p->ptr = pVar;

	s = getMem_C(strlen(szVarName) + 1);
	strcpy(s, szVarName);
	p->name = s;

	return (void*)p;
}


__declspec(dllexport)
void *register_foo(char* szFooName, void* pFoo, char* fooType){
	struct Var* p;
	char* s;

	if(
		szFooName == 0 ||
		pFoo == 0 ||
		fooType == 0
	){
		return 0;
	}
	
	for(p = foo_registry; p != 0; p = p->next){
		if(!strcmp(p->name, szFooName))
			return 0;
	}

	p = getMem_C(sizeof(struct Var));
	p->next = foo_registry;
	foo_registry = p;

	s = getMem_C(strlen(fooType) + 1);
	strcpy(s, fooType);
	p->type = s;

	p->ptr = pFoo;

	s = getMem_C(strlen(szFooName) + 1);
	strcpy(s, szFooName);
	p->name = s;

	return (void*)p;
}
//-----------------------------------------------------------------------









extern 
void* __stdcall get_mod_var(void *Ll, char *var, int var_type){
	struct Var* p;

	for(p = var_registry; p != 0; p = p->next){
		if(
			strcmp(p->name, var) == 0 &&
			(char)p->type == (char)var_type
		){
			return p->ptr;
		}
	}

	return 0;
}


extern 
void __stdcall set_mod_var(void *Ll, char *var, int var_type){
	struct Var* p;
	char vt=(char)var_type;
	int v;
	long double r;

	__asm{ mov v,eax}
	for(p = var_registry; p != 0; p = p->next){
		if(
			strcmp(p->name, var) == 0 &&
			(char)p->type == (char)var_type
		){
			if((vt&0xcf)==0x80){
				__asm{ fstp r }
				*(double*)p->ptr = r;
			}else if((vt&7)==7)
				strcpy((char*)p->ptr, v);
			else
				*(int*)p->ptr = v;
		}
	}
}


extern 
void* __stdcall call_mod_func(void *Ll, char *func, int n_par){
	struct Var* p;
	void* ret = 0;

	for(p = foo_registry; p != 0; p = p->next){
		if(
			strcmp(p->name, func) == 0 
//			&&
//			(char)p->type == (char)var_type
		){
			ret = ((void* (*)())p->ptr)();
//			__asm{
//				mov eax,n_par
//				lea eax,[eax*4]
//				add esp,eax
//			}
		}
	}
	return ret;
}



extern char * __stdcall get_mod_memb_name(void *lpMod_memb);
extern char * __stdcall get_mod_memb_doc(void *lpMod_memb);
extern void __stdcall get_mod_memb_type(void *lpMod_memb,char *sztype);
extern void __stdcall get_mod_memb_access(void *lpMod_memb,char *sztype);
extern int __stdcall get_mod_memb_val(void *lpMod_memb,void **val);
extern int __stdcall get_mod_memb_lppars_bin(void *lpMod_memb,void **pars);
extern int __stdcall get_mod_memb_lprets_bin(void *lpMod_memb,void **rets);


__declspec(dllexport)
char* get_mod_member_name(void* mod_member){
	return get_mod_memb_name(mod_member);
}


__declspec(dllexport)
char* get_mod_member_doc(void* mod_member){
	return get_mod_memb_doc(mod_member);
}


__declspec(dllexport)
char* get_mod_member_type(void* mod_member, char* type_buf){
	get_mod_memb_type(mod_member, type_buf);
	return type_buf;
}


__declspec(dllexport)
char* get_mod_member_access(void* mod_member, char* access_buf){
	get_mod_memb_access(mod_member, access_buf);
	return access_buf;
}


__declspec(dllexport)
void* get_mod_member_val(void* mod_member, char* pType){
	void* pval;
	int type = 0;

	if(pType == 0)
		return 0;

	type = get_mod_memb_val(mod_member, &pval);
	if(pval){
		type &= 0xc7;
		switch(type){
			case 0:
				pval = 0;
				break;

//			case 0x80:
//				lua_pushnumber(L,*((double*)lpval));
//			case 0x7:
//			case 0x87:
//				lua_pushstring(L,((char*)lpval));
//				break;
//			default:
//				lua_pushnumber(L,*((int*)lpval));
		}
	}else{
	}

	*pType = type;
	return pval;
}


__declspec(dllexport)
void set_mod_member_val(void* mod_member, void* pval){
	int vt = 0;
	union {
		int i;
		double f;
		char* s;
	} *v;

	if(mod_member == 0 || pval == 0)
		return;

	vt = get_mod_memb_val(mod_member, &v);
	if(v == 0)
		return;

	vt &= 0xc7;
	switch(vt){
		case 0:
			break;

		case 0x80:
			v->f = *((double*)pval);
			break;

		case 0x7:
		case 0x87:
			strcpy(v->s, (char*)pval);
			break;

		default:
			v->i = *((unsigned int*)pval);

	}

}


struct Rets{
	unsigned saved_ebp;
	struct Rets* prev;
};


#pragma optimize( "", off )

/*__declspec(thread)*/ struct Rets* rets = 0;



__declspec(dllexport)
int call_mod_member(void* mod_member, int cpar, void** vpar){
	int z, i;
	void **modmemb;
	void *lpval;
	char *lppars;
	char *vs;
	double vr;
	int vi;
	void **vu;
	int ri;
	unsigned saved_esp;
	struct Rets saved;
	char c;

	modmemb = &mod_member;

	z = get_mod_memb_val(*modmemb, &lpval);
	if(!(z&0xc7)){
/*
		__asm{
			lea eax,[old_ebp_restore+1]
			mov saved_esp,eax
		}
		VirtualProtect(saved_esp & (~(0x1000-1)), 0x1000-2, PAGE_EXECUTE_READWRITE	, &saved_esp);
*/
		z = get_mod_memb_lppars_bin(*modmemb, &lppars);
		if(z < cpar)
			return -1;

		lppars += z-1;

		__asm{
			pusha
			mov saved_esp,esp
		}
		saved.prev = rets;
		rets = &saved;
		__asm{
			lea eax,[saved]
			mov [eax],ebp
		}

	// set SEH b
		__asm{
			call mmc_set_SEH_handler

			mov esp,[esp+8]
			jmp mmc_error_in_foo
		}
	mmc_set_SEH_handler:
		__asm{
			xor eax,eax
			push dword ptr fs:[eax]
			mov fs:[eax],esp
		}
	// set SEH e

		for(i = z - 1; z; lppars--, z--, i--){
			c = *lppars;
			switch(c&0xc7){
				case 0x80:
					vr = *((double*)vpar[i]);
					__asm{
						push eax
						push eax
						fld vr
						fstp qword ptr [esp]
					}
					break;

				case 0x7:
				case 0x87:
					vs = (char*)vpar[i];
					__asm{
						push vs
					}
					break;

				default:
					vi = (int)vpar[i];
					__asm{
						push vi
					}

			}
		}
//		saved.prev = rets;
//		rets = &saved;
////int 3
//		__asm{
//			lea eax,[saved]
//			mov [eax],ebp
		__asm{
			call lpval
		}
	old_ebp_restore:
	// SEH reset b
		__asm{
			mov ebp,eax
			
			xor eax,eax
			pop dword ptr fs:[eax]
			pop eax

			mov eax,ebp
		}
	// SEH reset e

		__asm{
			mov ebp,rets
			mov ebp,ds:[ebp]
			mov esp,saved_esp
			mov ri,eax
			popa
		}
		rets = saved.prev;
/*
		z=get_mod_memb_lprets_bin(*modmemb,&lppars);
		if(z == 0 || lppars == 0)
			lua_pushnil(L);
		else{
			c = *lppars;
			switch(c){
				case 0x87:
				case 0x7:
					if(ri == 0)
						lua_pushnil(L);
					else
						lua_pushstring(L, (char*)ri);
					break;
				default:
					lua_pushnumber(L, (double)ri);
			}
		}
		return 1;
*/
		return ri;
	}

	return -1;

	mmc_error_in_foo:
//		Error("Error! exception is occured inside asm module call\n");
		__asm{
			mov eax,-1
		}
		goto old_ebp_restore;
}

#pragma optimize( "", on )



extern char * __stdcall get_mod_name(void *lpMod_link);
extern char * __stdcall get_mod_doc(void *lpMod_link);
extern void * __stdcall get_mod_func(void *lpMod_link);
extern char * __stdcall get_module_src(void *lpMod_link);
extern void * __stdcall get_mod_memb_idx(void *lpMod_link,int idx);
extern void * __stdcall get_mod_memb_by_name(void *lpMod_link,char *lpszModMembName);


__declspec(dllexport)
void* get_module_member_by_index(void* mod, int idx){
	return get_mod_memb_idx(mod, idx);
}


__declspec(dllexport)
void* get_module_member_by_name(void* mod, char* member_name){
	return get_mod_memb_by_name(mod, member_name);
}


__declspec(dllexport)
char* get_module_name(void* mod){
	return get_mod_name(mod);
}


__declspec(dllexport)
char* get_module_doc(void* mod){
	return get_mod_doc(mod);
}


__declspec(dllexport)
char* get_module_source(void* mod){
	return get_module_src(mod);
}


__declspec(dllexport)
void* get_module_main_foo(void* mod){
	return get_mod_func(mod);
}

/*
__declspec(dllexport)
void* get_mod_memb_by_name(void* mod, char* member_name){
	if(t==LUA_TSTRING){
		s=lua_tostring(L,2);
		if(!strcmp(s,szName)){
			lua_pushstring(L,get_mod_name(*mod));
			return 1;
		}else if(!strcmp(s,szComment)){
			lua_pushstring(L,get_mod_doc(*mod));
			return 1;
		}else if(!strcmp(s,"byname")){
//			lua_pushlightuserdata(L,0);
			p=lua_newuserdata(L,sizeof(void**));
			*p=*mod;
			lua_newtable(L);
				lua_pushstring(L,sz__index);
				lua_pushcfunction(L,Lmod_memb_by_name);
				lua_rawset(L,-3);
			lua_setmetatable(L,-2);
			return 1;
		}else if(!strcmp(s,szFunc)){
			lua_pushcfunction(L,get_mod_func(*mod));
			return 1;
		}else if(!strcmp(s,szSrc)){
			lua_pushstring(L,get_module_src(*mod));
			return 1;
		}

		return 0;
	}
}
*/






extern void * __stdcall get_mod_by_idx(int idx);
extern void * __stdcall get_mod_by_name(char *lpszModName);
extern int __stdcall get_mods_num();


__declspec(dllexport)
void* module_by_name(char* mod_name){
	return get_mod_by_name(mod_name);
}


__declspec(dllexport)
int modules_count(){
	return get_mods_num();
}






//----------------------------------------------------------------------------
extern void __stdcall __enter__();
extern void __stdcall __exit__();

BOOL APIENTRY DllMain(HANDLE hModule, 
                      DWORD  ul_reason_for_call, 
                      LPVOID lpReserved)
{
	switch( ul_reason_for_call ) {
		case DLL_PROCESS_ATTACH:
			__enter__();
			break;
			
		case DLL_THREAD_ATTACH:
			break;
			
		case DLL_THREAD_DETACH:
			break;
			
		case DLL_PROCESS_DETACH:
			__exit__();
			break;
			
	}
	return TRUE;
}


